--
-- Copyright (C) 2005 dahua Technologies, All Rights Reserved.
-- 2006-4-25 15:54 Z:\wjj\ven\152\DAHUA\Install.lua
-- 2006-9-21  modified by zhongjl for new partition
--

-- Flash 块的大小为64K
local flashSectorSize = 0x10000;

local Installer = {};
Installer.TotalSize      = 0;    -- 总的要写到Flash中的数据大小
Installer.InProgressSize = 0;	 -- 用来在升级过程中控制进度

function Installer:do_cmd(cmd)
	local ret;
	if (mtd.run_cmd) then
		ret = mtd.run_cmd(cmd);
		print(string.format("Installer:do_cmd mtd.run_cmd cmd=%s ret=%d \n", cmd, ret));
	else
		ret = os.execute(cmd);
		print(string.format("Installer:do_cmd os.execute cmd=%s ret=%d \n", cmd, ret));
	end
	return ret == 0;
end

-- 通知上层应用程序升级的进度信息
-- params:
--    无
-- return:
--    无
function Installer:notify()
	self.InProgressSize = self.InProgressSize + flashSectorSize;

	local progress = self.InProgressSize / self.TotalSize * 100;
	if(progress > 100) then
		progress = 100;
	end;
	progress = tonumber(string.format("%d", progress));

	-- 注意调用方式,不是self::callback,否则会导致回调出错
	self.callback(0x01, progress);
end

-- 根据应用程序提供的信息判断是否可以升级
-- params:
--    无
-- return:
--    成功返回True, 失败返回False以及失败的原因
function Installer:preInstall()
	-- 这里我们需要考虑那些因数？
	-- 硬件版本号 ？
	-- 原有软件的版本号？
	--
	local board = Global.Hardware.board;
	local hwproduct;
	local hwchannel;
	local hwversion;
	local hwfunction;

	print(string.format("Checking hardware information,board name:%s version:%s",
		board.name, board.version));
	if((board.name ~= "XVR5x04") or ((board.version ~= "1.00") and (board.version ~= "1.01"))) then
		return false, "Invalid board";
	end

	--升级程序直接从硬件中取值。意义如下
	--hwproduct取值如下: 2 -- LB; 3 -- LBN; 4 -- LB_ATM; 5 -- GBE; 6 -- LK; 7 -- LS
	--hwproduct取值如下: 2 -- IPC4X5; 3 -- A6; 4 -- A8; 5 -- IPVM;
	--hwchannel取值如下: x -- x chans
	--hwfunction取值如下: 0 -- AUDIO; 1 -- MATRIX; 3 -- LOOP

	if(mtd.getinfo)then
		hwproduct,hwchannel,hwversion,hwfunction = mtd.getinfo();
		print(string.format("Checking hardware id info: product = %x; channel = %x; version = %x; function = %x",
			hwproduct,hwchannel,hwversion,hwfunction));
		if((hwproduct ~= 3) and (hwproduct ~= 4)) then
			print(string.format("product not match"));
			return false, "Invalid board";
		end
	end

	-- 这里我们需要校验不同板本的板子程序是否可以通用,
	-- 如目前LB 1.22的与 LB 2.00的板子就不能通用
	-- if(board.version ~= "1.22") then
	--  print(string.format("XXXXXXXXXXXX"));
	--	return false, "Invalid board version";
	-- end

	local vendor = Global.Vendor;

	if(vendor.Name ~= 'General_XVR5x04') then
		return false, "Invalid vendor";
	end

	return true;
end

-- 升级完成后的处理,如控制系统重启
-- params:
--    无
-- return:
--    无
function Installer:postInstall()
end

-- 升级Flash分区,对于不同的应用,升级的处理可能不一样
-- params	:
-- part		: 表格,包含分区的起始位置以及结束位置
-- filename	: 在升级包中的文件名
-- return	: 无
function Installer:updatePart(part, filename)
	local myfile = self.ZipArchive:open(filename);
	local write_times = 0;
	-- 如果该文件打不开,则不需要对此部分进行升级
	if(not myfile) then
		print(string.format("%s not exist", filename));
		return ;
	end

	local addr   = part.baseAddr;
	local endAddr= part.endAddr;
	local data;
	local fldata;
	local write_times1 = 0;
	
	print(string.format("Start Update : %s ", filename));
	-- 跳过前面64字节的头,在新的升级程序版本中不需要对此进行校验
	myfile:seek("set", 64);
	while(addr < endAddr) do
	
		fldata = mtd.read(addr,flashSectorSize);
		data = myfile:read(flashSectorSize);    -- 读入一块Flash扇区大小的数据
		-- if (fldata and data and (fldata ~= data)) then
		-- 当fldata为空的时候为老的程序，直接擦除

		if(data) then
			if((not fldata) or (data and (fldata ~= data))) then
				mtd.erase(addr);
				mtd.write(addr, data);
				write_times = write_times+1;
				fldata = mtd.read(addr,flashSectorSize);
				if((not fldata) or (data and (fldata ~= data))) then
					mtd.erase(addr);
					mtd.write(addr, data);
					write_times1 = write_times1 + 1;
				end			
			end
		  self:notify();
		  addr   = addr   + flashSectorSize;
		else
		  addr = endAddr;
		end
	end
	print(string.format("==========> Write Times : %s  write_times1 : %s<==========", write_times, write_times1));
	myfile:close();
end

function Installer:InstallPlayer(filename)
	local myfile = self.ZipArchive:open(filename);

	-- 如果该文件打不开,则不需要对此部分进行升级
	if(not myfile) then
		return ;
	end

	--
	-- TODO:
	-- 读入自动播放器的内容，通过IDE接口写入到硬盘
	--
end

--对压缩包里的img文件进行校验，校验正确返回1，错误返回0
function Installer:crc32check( filename)
	local myfile = self.ZipArchive:open(filename);
	
	-- 如果该文件打不开(即无此文件),则不需要对此部分进行升级
	if(not myfile) then
		print(string.format("==>Installer:crc32check not myfile %s , no need to check", filename));
		return 1;
	end
	
	-- 如果应用程序无校验接口,则不需校验，直接返回成功，以兼容以前的应用程序
	if(not mtd.check_init) then
		print(string.format("==>Installer:crc32check not mtd.check_init, no need to check") );
		return 1	
	end	
	if(not mtd.crc32)then
		print(string.format("==>Installer:crc32check not mtd.crc32, no need to check"));
		return 1
	end
	
	local head = myfile:read(64);--读64个字节的头
	
	local filesize = 0;
	local filecrc32 = 0;
	print(string.format("==>Installermtd.check_init"));
	filesize, filecrc32 = mtd.check_init(head);-- 初始化
	print(string.format("==>Installer mtd.check_init over, filesize:%d filecrc:%x!", filesize, filecrc32));
	local checksum = 0;
	local readlen =0;
	local data;
	-- 对数据部分计算校验和
	while (readlen < filesize) do
		data = myfile:read(flashSectorSize);
		--print(string.format("Installer check crc32:%x ------%x!", checksum, readlen) );
		if (filesize - readlen < flashSectorSize) then
			checksum = mtd.crc32(checksum, data, filesize - readlen)
		else
			checksum = mtd.crc32(checksum, data, flashSectorSize)
		end
		readlen = readlen + flashSectorSize;
	end

	if (checksum ~= filecrc32) then
		print(string.format("==>error: Installer check (%s)crc32 error:%x ------%x!", filename, checksum, filecrc32));
		return 0;
	end
	print(string.format("==>correct: Installer check(%s) crc32 correct:%x ------%x!", filename, checksum, filecrc32));
	return 1;
end


-- 在这里控制整个升级过程
-- params:
--    无
-- return:
--    成功返回True,失败返回Flase以及错误原因
function Installer:execute()
	--[[
	 分区配置信息表,来自知识库 "Flash 分区规划"
	 0xa0000000 － 0xa0050000  320K     armboot                                 u-boot
	 0xa0050000 -- 0xa0060000  64k 系统预留
	 0xa0060000 － 0xa0DA0000  13568K   kernel + root                          romfs
	 0xa0DA0000 － 0xa0F40000  1664K    web                                     web
	 0xa0F40000 － 0xa0F60000  128K     custom                                  custom
	 0xa0F60000 － 0xa0F80000  128k     logo                                    logo
	 0xa0F80000 － 0xa1000000  512k    config                                  
	]]
	
	local flashPartions =
	{
		boot		= { baseAddr = 0xa0000000  , endAddr = 0xa0050000 },
		rootfs		= { baseAddr = 0xa0060000  , endAddr = 0xa0DA0000 },
		web			= { baseAddr = 0xa0DA0000  , endAddr = 0xa0F40000 },
		custom  	= { baseAddr = 0xa0F40000  , endAddr = 0xa0F60000 },
		logo		= { baseAddr = 0xa0F60000  , endAddr = 0xa0F80000 },
		config		= { baseAddr = 0xa0F80000  , endAddr = 0xa1000000 },
	}

	-- self.ZipArchive 这个变量由外部程序设置,如果没有设置程序不应该运行到这里
	assert(self.ZipArchive);
	local zfile = self.ZipArchive;

	local ret, info = self:preInstall();
	if(not ret) then
		return false, info;
	end

	-- 计算要升级的文件大小以及打印升级文件清单
	local TotalSize = 0;
	print("==>Files in archive");
	for file in zfile:files() do
		print(file.filename);
		TotalSize = TotalSize + file.uncompressed_size;
	end
	self.TotalSize  = TotalSize;     -- 总的文件大小

	-- 删除/mnt/mtd/ssl路径
	os.remove("/mnt/mtd/ssl/ca.key");
	os.remove("/mnt/mtd/ssl/ca.crt");
	os.remove("/mnt/mtd/ssl/cacert.pem");
	os.remove("/mnt/mtd/ssl/privkey.pem");
	os.remove("/mnt/mtd/ssl/pubkey.pem");
	os.remove("/mnt/mtd/ssl");
	
	mtd.init();

	if ( 1 ~= (self:crc32check( "u-boot.bin.img"))
	     or 1 ~= (self:crc32check("romfs-x.cramfs.img")) 
		or 1 ~= (self:crc32check("user-x.cramfs.img")) 
	     or 1 ~= (self:crc32check("web-x.cramfs.img")) 
	     or 1 ~= (self:crc32check("slave-x.cramfs.img")) 
	     or 1 ~= (self:crc32check("logo-x.cramfs.img")) 
	     or 1 ~= (self:crc32check( "custom-x.cramfs.img")) ) then
		print("==>Upgrade: crc32check error!");
		return false;
	end

	self:updatePart(flashPartions["boot"],	"u-boot.bin.img");
	--mtd.saveenv();
	self:updatePart(flashPartions["rootfs"],	"romfs-x.cramfs.img");
	--self:updatePart(flashPartions["user"],		"user-x.cramfs.img");
	self:updatePart(flashPartions["web"],		"web-x.cramfs.img");
	--self:updatePart(flashPartions["slave"],		"slave-x.cramfs.img");
	self:updatePart(flashPartions["custom"],	"custom-x.cramfs.img");
	self:updatePart(flashPartions["logo"],		"logo-x.cramfs.img");
	--self:InstallPlayer("autoplayer.bin");
	self:postInstall();
	print("==>Upgrade finished.");
	return true;
end

return Installer;
